use crate::float::parse_float;

/// 符号类型宽度
/// 数组/指针中：
/// tar为所指目标类型
/// 维度值若为-1，则表示x层为指针；若为自然数，则表示数组
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Width {
    Void,
    I1,
    I8,
    I32,
    I64,
    Float,
    Arr{tar: Box<SymType>, dims: Vec<i32>}, 
}

/// 符号类型：宽度+可变性
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct SymType { 
    pub width: Width,
    pub is_const: bool,
}

/// 符号值
#[derive(Debug, Clone, PartialEq)]
pub enum SymVal {
    Void,
    I32(String),
    Float(String),
    Func(SymType, Vec<SymType>),
}

impl SymType {
    /// 生成新的符号类型
    pub fn new(width: Width, is_const: bool) -> Self{
        SymType {
            width: width,
            is_const: is_const,
        }
    }

    /// 获取符号格式
    pub fn type_name(&self) -> String {
        self.width.type_name()
    }

    pub fn width_num(&self) -> usize {
        self.width.width_num()
    }
}

/// 根据维度和目标类型，获取对应格式
pub fn dims_name(tar: &SymType, dims: &[i32]) -> String {
    let tar_name = tar.type_name();
    let mut res = String::new();
    for dim in dims.iter() {
        if dim >= &0 {
            res += format!("[{} x ", dim).as_str();
        }
    }
    res += tar_name.as_str();
    for dim in dims.iter().rev() {
        if dim >= &0 {
            res += "]";
        } else {
            res += "*";
        }
    }
    res
}

impl Width {
    pub fn type_name(&self) -> String {
        match self {
            Width::Void => String::from("void"),
            Width::I1 => String::from("i1"),
            Width::I8 => String::from("i8"),
            Width::I32 => String::from("i32"),
            Width::I64 => String::from("i64"),
            Width::Float => String::from("float"),
            Width::Arr{tar, dims} => {
                dims_name(tar.as_ref(), dims.as_slice())
            },
        }
    }

    pub fn width_num(&self) -> usize {
        match self {
            Width::I1 | Width::I8 | Width::I32 | Width::I64 | Width::Float => 4,
            Width::Arr{tar: _, dims: _} => 8,
            _ => panic!("Do not support"),
        }
    }
}

/// 根据符号类型和String值val，生成对应的SymVal
pub fn make_sym_val(ty: &SymType, val: &String) -> SymVal {
    match ty.width {
        Width::I32 => SymVal::I32(String::from(val)),
        Width::Float => SymVal::Float(String::from(val)),
        _ => panic!("TODO"),
    }
}

/// 从SymVal中取出str转换为String值
pub fn fetch_sym_val(sym_val: &SymVal) -> String {
    match &sym_val {
        SymVal::I32(val) => String::from(val),
        SymVal::Float(val) => String::from(val),
        _ => panic!("{:?} TODO", sym_val),
    }
}

/// 根据符号的类型，检查值val是否为零
pub fn num_is_zero(ty: &SymType, val: &String) -> bool {
    match ty.width {
        Width::I1 => {
            let num: i32 = val.parse().unwrap();
            num == 0
        },
        Width::I32 => {
            let num: i32 = val.parse().unwrap();
            num == 0
        },
        Width::Float => {
            let num: f32 = parse_float(val);
            num == 0.0
        },
        _ => panic!("{:?} TODO", ty),
    }
}

